﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System.Linq;
using System.Text;
using Input;
using FarseerPhysics.Dynamics;
using Physics;
using FarseerPhysics.Factories;
using FarseerPhysics;

/**
 * This class is responsible for the management of the game world.  
 * This includes Level Loading, AI, Sprite Management, Physics.  The
 * game will always have an instance of World, and this instance should
 * never be destroyed until the game exits.
 */
namespace MapLibrary
{
    public class GameWorld : KeyboardInputListener, MouseInputListener
    {
        private Vector2 Gravity = new Vector2(0.0f, ConvertUnits.ToSimUnits(5));
        
        /**
         * The mGraphicsDevice is the drawing device for the game.  All game objects will
         * get drawn through this graphics device.
         */
        private GraphicsDevice mGraphicsDevice;

        /**
         * This is the content manager.  It is used to load levels, maps, sprites, etc.
         */
        private ContentManager mContentManager;

        /**
         * This sprite batch is the parent sprite batch where all game sprites and textures
         * will be drawn.
         */
        private SpriteBatch mSpriteBatch;

        /**
         * The viewport for the world.  All level objects will be drawn through
         * this viewport.  The viewports position will be updated by the WASD keys.
         */
        private WorldViewport mViewport;

        /**
         * This is the level that the world currently has loaded.
         */
        private Level mCurrentLevel;

        /**
         * This is the overarching level editor.  There is a constant reference to this
         * in the game world.  The level editor will get activated upon pressing the "z"
         * key by the game world by mEditor.Edit(Level).
         */
        private LevelEditor mEditor;

        private World physicsWorld;

        /**
         * Constructs a new World object on the specified device and content manager.  All
         * game world content will be loaded on the specified content manager, and all game
         * world graphics objects will be drawn on the specified graphics device.
         */
        public GameWorld(GraphicsDevice device, ContentManager manager)
        {
            mGraphicsDevice = device;
            mContentManager = manager;

            mSpriteBatch = new SpriteBatch(mGraphicsDevice);
            mViewport = new WorldViewport(device);
            mEditor = new LevelEditor(mGraphicsDevice, mViewport, manager);

            physicsWorld = new World(Gravity);
            physicsWorld.ContactManager.OnBroadphaseCollision += MyOnBroadphaseCollision;
        }

        public void MyOnBroadphaseCollision(ref FixtureProxy pf1, ref FixtureProxy fp2)
        {
            return;
        }

        public void Update(GameTime time)
        {
            if (!mEditor.Enabled)
            {
                physicsWorld.Step(Math.Min((float)time.ElapsedGameTime.TotalMilliseconds * 0.001f, (1f / 30f)));

                mCurrentLevel.UpdateAnimations();
            }
            else
            {
            }
        }

        /**
         * Draws all game world objects on the graphics device.  These objects are draw
         * through the game worlds specific sprite batch.  Note that if the level editor
         * is enabled, the level editor elements will be drawn as well.
         */
        public void Draw()
        {
            mSpriteBatch.Begin();

            if (!mEditor.Enabled)
                mCurrentLevel.Draw(mSpriteBatch, mViewport);
            else
                mEditor.Draw(mSpriteBatch);

            mSpriteBatch.End();
        }

        /**
         * Prepares the World for use in the game.
         */
        public void LoadWorld()
        {
            mEditor.LoadEditor(mContentManager);

            //temporary
            mCurrentLevel = mContentManager.Load<Level>(@"testingLevelLoad");
            //mCurrentLevel = new Level();
            //mCurrentLevel.LoadLevel(mContentManager);

            RegisterCollisionObjects();
        }

        /// <summary>
        /// Takes all of the CollisionObjects in the Level and turns them
        /// into Bodies and fixtures for World.
        /// </summary>
        private void RegisterCollisionObjects()
        {
            foreach (CollisionObject obj in mCurrentLevel.CollisionObjectsEnum())
            {
                obj.Body = BodyFactory.CreatePolygon(
                    physicsWorld,
                    ConvertUnits.ToSimUnits(obj.Hull),
                    1.0f,
                    ConvertUnits.ToSimUnits(obj.Hull.GetCentroid())
                    );
            }
        }

        public void UpdateMouseInput(GameInput Input)
        {
            
        }

        /**
         * Modify the world through the keyboard input.  Right now this mostly involves
         * moving the world's viewport and enabling editing.
         */
        public void UpdateKeyboardInput(GameInput Input)
        {
            if (Input.IsKeyDown(Keys.A))
                mViewport.MoveLeft();

            if (Input.IsKeyDown(Keys.W))
                mViewport.MoveUp();

            if (Input.IsKeyDown(Keys.D))
                mViewport.MoveRight();

            if (Input.IsKeyDown(Keys.S))
                mViewport.MoveDown();

            if (Input.IsKeyPressed(Keys.Z))
            {
                if (mEditor.Enabled)
                    mEditor.Disable();
                else
                    mEditor.LoadEditing(mCurrentLevel, mContentManager);
            }
        }

        /**
         * Also registers the editor as a mouse listener.
         */
        public void OnMouseRegister(GameInput Input)
        {
            Input.AddMouseListener(mEditor);
        }

        /**
         * Also registers the editor as a keyboard listener.
         */
        public void OnKeyboardRegister(GameInput Input)
        {
            Input.AddKeyboardListener(mEditor);
        }
    }
}
